package ie.dkit.java3Demulation.transforming;

import ie.dkit.java3Demulation.objects3d.Point3D;

public class Transformer {
	private static class Matrix {

		double[][] content;

		Matrix(double[][] content) {
			this.content = content;
		}

		void multiplicate(Point3D with) {
			double[] withPoint = { with.getX(), with.getY(), with.getZ(), 1 };
			double[] result = { 0, 0, 0, 0 };
			for (int i = 0; i < 4; i++) {
				for (int j = 0; j < 4; j++) {
					result[i] += withPoint[j] * content[i][j];
				}
			}
			with.setX(result[0]);
			with.setY(result[1]);
			with.setZ(result[2]);
		}
	}

	public static void translate(Point3D point3D, double x, double y, double z) {
		Matrix translate = new Matrix(new double[][] { { 1, 0, 0, x },
				{ 0, 1, 0, y }, { 0, 0, 1, z }, { 0, 0, 0, 1 } });
		translate.multiplicate(point3D);
	}

	public static void translate(Transformable transformable, double x,
			double y, double z) {
		for (Point3D point3D : transformable.getPoints()) {
			translate(point3D, x, y, z);
		}
	}

	/**
	 * Scales the point with relative values
	 * 
	 * @param point3D
	 * @param sx
	 * @param sy
	 * @param sz
	 */
	public static void scale(Point3D point3D, double sx, double sy, double sz) {
		// TODO: fix this
		double px = point3D.getX();
		double py = point3D.getY();
		double pz = point3D.getZ();
		Matrix scale = new Matrix(new double[][] { { sx, 0, 0, 0 },
				{ 0, sy, 0, 0 }, { 0, 0, sz, 0 }, { px*(1-sx), py*(1-sy), pz*(1-sz), 1 } });
		scale.multiplicate(point3D);
	}

	public static void scale(Transformable transformable, double sx, double sy,
			double sz) {
		Point3D rotationPoint = transformable.getRotationPoint();
		boolean noRotationPoint = rotationPoint == null;
		for (Point3D point3D : transformable.getPoints()) {
			if (noRotationPoint) {
				scale(point3D, sx, sy, sz);
			} else {
				translate(point3D, -rotationPoint.getX(),
						-rotationPoint.getY(), -rotationPoint.getZ());
				scale(point3D, sx, sy, sz);
				translate(point3D, rotationPoint.getX(), rotationPoint.getY(),
						rotationPoint.getZ());
			}
		}
	}

	public static void rotateZ(Point3D point3D, double a) {
		Matrix rotateZ = new Matrix(new double[][] {
				{ Math.cos(Math.toRadians(a)), -Math.sin(Math.toRadians(a)), 0,
						0 },
				{ Math.sin(Math.toRadians(a)), Math.cos(Math.toRadians(a)), 0,
						0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } });
		rotateZ.multiplicate(point3D);
	}

	public static void rotateZ(Transformable transformable, double a) {
		Point3D rotationPoint = transformable.getRotationPoint();
		boolean noRotationPoint = rotationPoint == null;
		for (Point3D point3D : transformable.getPoints()) {
			if (noRotationPoint) {
				rotateZ(point3D, a);
			} else {
				translate(point3D, -rotationPoint.getX(),
						-rotationPoint.getY(), -rotationPoint.getZ());
				rotateZ(point3D, a);
				translate(point3D, rotationPoint.getX(), rotationPoint.getY(),
						rotationPoint.getZ());
			}
		}
	}

	public static void rotateX(Point3D point3D, double a) {
		Matrix rotateX = new Matrix(new double[][] {
				{ 1, 0, 0, 0 },
				{ 0, Math.cos(Math.toRadians(a)), -Math.sin(Math.toRadians(a)),
						0 },
				{ 0, Math.sin(Math.toRadians(a)), Math.cos(Math.toRadians(a)),
						0 }, { 0, 0, 0, 1 } });
		rotateX.multiplicate(point3D);
	}

	public static void rotateX(Transformable transformable, double a) {
		Point3D rotationPoint = transformable.getRotationPoint();
		boolean noRotationPoint = rotationPoint == null;
		for (Point3D point3D : transformable.getPoints()) {
			if (noRotationPoint) {
				rotateX(point3D, a);
			} else {
				translate(point3D, -rotationPoint.getX(),
						-rotationPoint.getY(), -rotationPoint.getZ());
				rotateX(point3D, a);
				translate(point3D, rotationPoint.getX(), rotationPoint.getY(),
						rotationPoint.getZ());
			}
		}
	}

	public static void rotateY(Point3D point3D, double a) {
		Matrix rotateY = new Matrix(new double[][] {
				{ Math.cos(Math.toRadians(a)), 0, Math.sin(Math.toRadians(a)),
						0 },
				{ 0, 1, 0, 0 },
				{ -Math.sin(Math.toRadians(a)), 0, Math.cos(Math.toRadians(a)),
						0 }, { 0, 0, 0, 1 } });
		rotateY.multiplicate(point3D);
	}

	public static void rotateY(Transformable transformable, double a) {
		Point3D rotationPoint = transformable.getRotationPoint();
		boolean noRotationPoint = rotationPoint == null;
		for (Point3D point3D : transformable.getPoints()) {
			if (noRotationPoint) {
				rotateY(point3D, a);
			} else {
				translate(point3D, -rotationPoint.getX(),
						-rotationPoint.getY(), -rotationPoint.getZ());
				rotateY(point3D, a);
				translate(point3D, rotationPoint.getX(), rotationPoint.getY(),
						rotationPoint.getZ());
			}
		}
	}
}
